package com.rtsapp.server.network.protocol.rpc.utils;

import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by admin on 15-12-4.
 */
public final class RPCCodeGenerator {
    private static final String NEW_LINE = "\r\n";

    private static final String packageStr = "\\s*package\\s+(\\S+);\\s*$";

    private static final String varStr = "\\s*(public)?\\s*(static)?\\s*(final)\\s*(\\S+)\\s+(\\S+)\\s*=\\s*(\\S+)\\s*;\\s*$";

    private static final String interfaceStr = "\\s*(public)?\\s*(interface)?\\s*(\\S+)\\s*\\{\\s*$";
    private static final Pattern interfacePattern = Pattern.compile(interfaceStr);

    private static final String methodStr = "\\s*(public)?\\s*(abstract)?\\s*(\\S+)\\s+([^\\s\\(]+)(\\(.*\\))\\s*;\\s*";
    private static final Pattern methodPattern = Pattern.compile(methodStr);

    private static final String emptyParamPattern = "\\(\\s*\\)";

    public static void generateAysnApi(String apiFile, String aysnApiFile) {

        try {

            WriteVisitor visitor = new WriteVisitor( aysnApiFile, "", new AyncApiFilter() );
            visitRpcApiFile( apiFile, visitor);
            visitor.flushAndClose();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void generateAysnApiImpl(String apiFile, String aysnApiFile) {

        try {

            WriteVisitor visitor = new WriteVisitor( aysnApiFile, "", new AyncApiImplFilter() );
            visitRpcApiFile( apiFile, visitor);
            visitor.flushAndClose();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void generateAysnApiImplForSync(String apiFile, String aysnApiFile) {

        try {

            WriteVisitor visitor = new WriteVisitor( aysnApiFile, "", new AyncApiImplSyncFilter() );
            visitRpcApiFile( apiFile, visitor);
            visitor.flushAndClose();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void visitRpcApiFile(String apiFile, IRPCAPIVisitor visitor) {

        try {
            BufferedReader br = new BufferedReader(new FileReader(apiFile));

            String line = null;
            while ((line = br.readLine()) != null) {

                if (line.matches(packageStr)) {
                    visitor.visitPackage(line);
                } else if (line.matches(interfaceStr)) {
                    visitor.visitInterface(line);
                } else if (line.matches(methodStr)) {
                    visitor.visitMethod(line);
                } else if (line.matches(varStr)) {
                    visitor.visitVar(line);
                } else {
                    visitor.visitEmpty(line);
                }
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private interface IRPCAPIFiler {

        String processPackage(String packageLine);

        String processInterface(String interfaceLine);

        String processMethod(String methodLine);

        String processEmpty(String emptyLine);

        String processVar(String varLine);
    }

    private interface IRPCAPIVisitor {

        void visitPackage(String packageLine) throws IOException;

        void visitInterface(String interfaceLine) throws IOException;

        void visitMethod(String methodLine) throws IOException;

        void visitEmpty(String emptyLine) throws IOException;

        void visitVar(String varLine) throws IOException;

    }


    private static  class WriteVisitor implements IRPCAPIVisitor {

        private final  String packageName;
        private final Writer writer;
        private final IRPCAPIFiler filter;

        private WriteVisitor(  String fileName, String packageName , IRPCAPIFiler filter ) throws IOException {
            writer = new BufferedWriter( new FileWriter( fileName ) );
            this.packageName = packageName;
            this.filter = filter;
        }

        @Override
        public void visitPackage(String packageLine) throws IOException {
            getWriter().write(filter.processPackage(packageLine));
            getWriter().write(NEW_LINE);
        }

        @Override
        public void visitInterface(String interfaceLine) throws IOException {
            getWriter().write(getFilter().processInterface(interfaceLine));
            getWriter().write(NEW_LINE);
        }

        @Override
        public void visitMethod(String methodLine) throws IOException {
            getWriter().write(getFilter().processMethod(methodLine));
            getWriter().write(NEW_LINE);
        }

        @Override
        public void visitEmpty(String emptyLine) throws IOException {
            getWriter().write(getFilter().processEmpty(emptyLine));
            getWriter().write(NEW_LINE);
        }

        @Override
        public void visitVar(String varLine) throws IOException {
            getWriter().write(getFilter().processVar(varLine));
            getWriter().write(NEW_LINE);
        }

        public  Writer getWriter(){
            return writer;
        }

        public  IRPCAPIFiler getFilter(){
            return filter;
        }

        public void flushAndClose() throws IOException {
            writer.flush();
            writer.close();
        }
    }

    private static class AyncApiFilter implements IRPCAPIFiler {

        @Override
        public String processPackage(String packageLine) {
            return packageLine;
        }

        @Override
        public String processInterface(String line) {

            Matcher matcher = interfacePattern.matcher(line);
            StringBuilder sb = new StringBuilder();


            sb.append("import com.rtsapp.server.network.protocol.rpc.client.RPCCallback;");
            sb.append(NEW_LINE);
            sb.append(NEW_LINE);

            if( matcher.find() ) {

                String oldInterfaceName = matcher.group(3);
                String interfaceName = oldInterfaceName + "Async";
                sb.append(line.replace(oldInterfaceName, interfaceName));

            }

            return sb.toString();
        }

        @Override
        public String processMethod(String line) {

            Matcher matcher = methodPattern.matcher(line);

            if( matcher.find() ) {


                String returnValue = matcher.group(3);
                String methodName = matcher.group(4);
                line = line.replaceAll(returnValue + "\\s+" + methodName, "void " + methodName);

                String params = matcher.group(5);

                if (params.matches(emptyParamPattern)) {
                    line = line.replace(params, "( RPCCallback callback )");
                } else {
                    String newParams = params.replace(")", ", RPCCallback callback )");
                    line = line.replace(params, newParams);
                }
            }

            return line;
        }

        @Override
        public String processEmpty(String emptyLine) {
            return emptyLine;
        }

        @Override
        public String processVar(String varLine) {
            return "";
        }
    }


    private static class AyncApiImplFilter implements IRPCAPIFiler {


        @Override
        public String processPackage(String packageLine) {
            return packageLine;
        }

        @Override
        public String processInterface(String line) {

            Matcher matcher = interfacePattern.matcher(line);

            if( matcher.find() ) {

                StringBuilder sb = new StringBuilder();

                sb.append("import com.rtsapp.server.network.protocol.rpc.client.RPCCallback;");
                sb.append(NEW_LINE);
                sb.append("import com.rtsapp.server.network.protocol.rpc.client.impl.RPCClientProxy;");
                sb.append(NEW_LINE);
                sb.append(NEW_LINE);

                String oldInterfaceName = matcher.group(3);
                String interfaceName = oldInterfaceName + "AsyncImpl";
                if( oldInterfaceName.startsWith("I") ){
                    interfaceName = interfaceName.substring( 1 );
                }
                sb.append(line.replace(oldInterfaceName, interfaceName + " implements " + oldInterfaceName + "Async").replace("interface", "class"));


                sb.append("private final RPCClientProxy clientProxy;");
                sb.append(NEW_LINE);
                sb.append("private final String serviceName;");
                sb.append(NEW_LINE);

                sb.append("public ");
                sb.append(interfaceName);
                sb.append("(RPCClientProxy clientProxy, String serviceName) {");
                sb.append(NEW_LINE);
                sb.append("this.clientProxy = clientProxy;");
                sb.append(NEW_LINE);
                sb.append("this.serviceName = serviceName;");
                sb.append(NEW_LINE);
                sb.append("}");

                return sb.toString();

            }else{
                return line;
            }
        }

        @Override
        public String processMethod(String line) {
            Matcher matcher = methodPattern.matcher(line);

            if( matcher.find() ) {

                StringBuilder sb = new StringBuilder();
                String publicStr = matcher.group(1);
                String returnValue = matcher.group(3);
                String methodName = matcher.group(4);
                line = line.replaceAll(returnValue + "\\s+" + methodName, "void " + methodName).replace( "abstract","" );

                String params = matcher.group(5);
                String callParams = "";
                if (params.matches(emptyParamPattern)) {
                    line = line.replace(params, "( RPCCallback callback )");
                    callParams = "callback";
                } else {
                    String newParams = params.replace(")", ", RPCCallback callback )");
                    line = line.replace(params, newParams);

                    String tempStr = params.replace("(", "").replace(")", ",");

                    String paramPattern = "\\s*(\\S+)\\s+(\\S+)\\s*,";
                    Pattern p = Pattern.compile(paramPattern);
                    Matcher m = p.matcher(tempStr);
                    while (m.find()) {
                        callParams += m.group(2) + ",";
                    }
                    callParams += " callback ";
                }

                if( publicStr == null ){
                    sb.append( "public ");
                }

                sb.append(line.replace(";", "{"));
                sb.append(NEW_LINE);

                sb.append("clientProxy.call( serviceName, ");
                sb.append("\"" + methodName + "\",");
                sb.append(callParams);
                sb.append(");");
                sb.append(NEW_LINE);

                sb.append("}");
                sb.append(NEW_LINE);

                return sb.toString();
            }else{
                return line;
            }
        }

        @Override
        public String processEmpty(String emptyLine) {
            return emptyLine;
        }

        @Override
        public String processVar(String varLine) {
            return "";
        }
    }



    private static class AyncApiImplSyncFilter implements IRPCAPIFiler {


        @Override
        public String processPackage(String packageLine) {
            return packageLine;
        }

        @Override
        public String processInterface(String line) {

            Matcher matcher = interfacePattern.matcher(line);

            if( matcher.find() ) {

                StringBuilder sb = new StringBuilder();

                sb.append("import com.rtsapp.server.network.protocol.rpc.client.RPCCallback;");
                sb.append(NEW_LINE);
                sb.append(NEW_LINE);

                String oldInterfaceName = matcher.group(3);
                String interfaceName = oldInterfaceName + "AsyncImplForSync";
                if( interfaceName.startsWith("I") ){
                    interfaceName = interfaceName.substring( 1 );
                }

                sb.append(line.replace(oldInterfaceName, interfaceName + " implements " + oldInterfaceName + "Async" ).replace("interface", "class"));

                sb.append("private final " + oldInterfaceName + " service;");
                sb.append(NEW_LINE);

                sb.append("public ");
                sb.append(interfaceName);
                sb.append("(" + oldInterfaceName + " service) {");
                sb.append(NEW_LINE);
                sb.append("this.service = service;");
                sb.append(NEW_LINE);
                sb.append("}");

                return sb.toString();

            }else{
                return line;
            }
        }

        @Override
        public String processMethod(String line) {
            Matcher matcher = methodPattern.matcher(line);

            if( matcher.find() ){
                StringBuilder sb = new StringBuilder();

                String publicStr = matcher.group(1);
                String returnValue = matcher.group(3);
                String methodName = matcher.group(4);
                line = line.replaceAll(returnValue + "\\s+" + methodName, "void " + methodName).replace( "abstract", "" );

                String params = matcher.group(5);
                String callParams = "";
                if (params.matches(emptyParamPattern)) {
                    line = line.replace(params, "( RPCCallback callback )");
                    callParams = "";
                } else {
                    String newParams = params.replace(")", ", RPCCallback callback )");
                    line = line.replace(params, newParams);

                    String tempStr  = params.replace("(","").replace(")", ",");

                    String paramPattern = "\\s*(\\S+)\\s+(\\S+)\\s*,";
                    Pattern p = Pattern.compile( paramPattern  );
                    Matcher m = p.matcher( tempStr );
                    while( m.find() ){
                        callParams += m.group( 2  ) + ",";
                    }
                    callParams = callParams.substring( 0, callParams.length() - 1 );
                }

                if( publicStr == null ){
                    sb.append( "public " );
                }
                sb.append( line.replace(";", "{" ) );
                sb.append( NEW_LINE );

                if( returnValue.trim().equals( "void" ) ){

                    sb.append( "service." );
                    sb.append( methodName );
                    sb.append( "(");
                    sb.append( callParams );
                    sb.append( ");" );
                    sb.append( NEW_LINE );

                    sb.append( "callback.onCompleted( null );");
                    sb.append( NEW_LINE );
                }else{
                    sb.append( "callback.onCompleted( service." );
                    sb.append( methodName );
                    sb.append( "(");
                    sb.append( callParams );
                    sb.append( ")" );
                    sb.append( ");");
                    sb.append( NEW_LINE );
                }

                sb.append( "}" );
                sb.append( NEW_LINE );

                return sb.toString();

            }else{
                return line;
            }

        }

        @Override
        public String processEmpty(String emptyLine) {
            return emptyLine;
        }

        @Override
        public String processVar(String varLine) {
            return "";
        }
    }


}
